/*
 * Globus Toolkit Public License (GTPL)
 *     
 * Copyright (c) 1999 University of Chicago and The University of 
 * Southern California. All Rights Reserved.
 * 
 *  1) The "Software", below, refers to the Globus Toolkit (in either
 *     source-code, or binary form and accompanying documentation) and a
 *     "work based on the Software" means a work based on either the
 *     Software, on part of the Software, or on any derivative work of
 *     the Software under copyright law: that is, a work containing all
 *     or a portion of the Software either verbatim or with
 *     modifications.  Each licensee is addressed as "you" or "Licensee."
 * 
 *  2) The University of Southern California and the University of
 *     Chicago as Operator of Argonne National Laboratory are copyright
 *     holders in the Software.  The copyright holders and their third
 *     party licensors hereby grant Licensee a royalty-free nonexclusive
 *     license, subject to the limitations stated herein and
 *     U.S. Government license rights.
 * 
 *  3) A copy or copies of the Software may be given to others, if you
 *     meet the following conditions:
 * 
 *     a) Copies in source code must include the copyright notice and
 *        this license.
 * 
 *     b) Copies in binary form must include the copyright notice and
 *        this license in the documentation and/or other materials
 *        provided with the copy.
 * 
 *  4) All advertising materials, journal articles and documentation
 *     mentioning features derived from or use of the Software must
 *     display the following acknowledgement:
 * 
 *     "This product includes software developed by and/or derived from
 *     the Globus project (http://www.globus.org/)."
 * 
 *     In the event that the product being advertised includes an intact
 *     Globus distribution (with copyright and license included) then
 *     this clause is waived.
 * 
 *  5) You are encouraged to package modifications to the Software
 *     separately, as patches to the Software.
 * 
 *  6) You may make modifications to the Software, however, if you
 *     modify a copy or copies of the Software or any portion of it,
 *     thus forming a work based on the Software, and give a copy or
 *     copies of such work to others, either in source code or binary
 *     form, you must meet the following conditions:
 * 
 *     a) The Software must carry prominent notices stating that you
 *        changed specified portions of the Software.
 * 
 *     b) The Software must display the following acknowledgement:
 * 
 *        "This product includes software developed by and/or derived
 *         from the Globus Project (http://www.globus.org/) to which the
 *         U.S. Government retains certain rights."
 * 
 *  7) You may incorporate the Software or a modified version of the
 *     Software into a commercial product, if you meet the following
 *     conditions:
 * 
 *     a) The commercial product or accompanying documentation must
 *        display the following acknowledgment:
 * 
 *        "This product includes software developed by and/or derived
 *         from the Globus Project (http://www.globus.org/) to which the
 *         U.S. Government retains a paid-up, nonexclusive, irrevocable
 *         worldwide license to reproduce, prepare derivative works, and
 *         perform publicly and display publicly."
 * 
 *     b) The user of the commercial product must be given the following
 *        notice:
 * 
 *        "[Commercial product] was prepared, in part, as an account of
 *         work sponsored by an agency of the United States Government.
 *         Neither the United States, nor the University of Chicago, nor
 *         University of Southern California, nor any contributors to
 *         the Globus Project or Globus Toolkit nor any of their employees,
 *         makes any warranty express or implied, or assumes any legal
 *         liability or responsibility for the accuracy, completeness, or
 *         usefulness of any information, apparatus, product, or process
 *         disclosed, or represents that its use would not infringe
 *         privately owned rights.
 * 
 *         IN NO EVENT WILL THE UNITED STATES, THE UNIVERSITY OF CHICAGO
 *         OR THE UNIVERSITY OF SOUTHERN CALIFORNIA OR ANY CONTRIBUTORS
 *         TO THE GLOBUS PROJECT OR GLOBUS TOOLKIT BE LIABLE FOR ANY
 *         DAMAGES, INCLUDING DIRECT, INCIDENTAL, SPECIAL, OR CONSEQUENTIAL
 *         DAMAGES RESULTING FROM EXERCISE OF THIS LICENSE AGREEMENT OR
 *         THE USE OF THE [COMMERCIAL PRODUCT]."
 * 
 *  8) LICENSEE AGREES THAT THE EXPORT OF GOODS AND/OR TECHNICAL DATA
 *     FROM THE UNITED STATES MAY REQUIRE SOME FORM OF EXPORT CONTROL
 *     LICENSE FROM THE U.S. GOVERNMENT AND THAT FAILURE TO OBTAIN SUCH
 *     EXPORT CONTROL LICENSE MAY RESULT IN CRIMINAL LIABILITY UNDER U.S.
 *     LAWS.
 * 
 *  9) Portions of the Software resulted from work developed under a
 *     U.S. Government contract and are subject to the following license:
 *     the Government is granted for itself and others acting on its
 *     behalf a paid-up, nonexclusive, irrevocable worldwide license in
 *     this computer software to reproduce, prepare derivative works, and
 *     perform publicly and display publicly.
 * 
 * 10) The Software was prepared, in part, as an account of work
 *     sponsored by an agency of the United States Government.  Neither
 *     the United States, nor the University of Chicago, nor The
 *     University of Southern California, nor any contributors to the
 *     Globus Project or Globus Toolkit, nor any of their employees,
 *     makes any warranty express or implied, or assumes any legal
 *     liability or responsibility for the accuracy, completeness, or
 *     usefulness of any information, apparatus, product, or process
 *     disclosed, or represents that its use would not infringe privately
 *     owned rights.
 * 
 * 11) IN NO EVENT WILL THE UNITED STATES, THE UNIVERSITY OF CHICAGO OR
 *     THE UNIVERSITY OF SOUTHERN CALIFORNIA OR ANY CONTRIBUTORS TO THE
 *     GLOBUS PROJECT OR GLOBUS TOOLKIT BE LIABLE FOR ANY DAMAGES,
 *     INCLUDING DIRECT, INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES
 *     RESULTING FROM EXERCISE OF THIS LICENSE AGREEMENT OR THE USE OF
 *     THE SOFTWARE.
 * 
 *                               END OF LICENSE
 */
package org.griphyn.vdl.annotation;

import java.io.Reader;
import java.io.IOException;
import java.io.LineNumberReader;
import org.griphyn.vdl.Chimera;

/**
 * Implements the scanner for reserved words and other tokens that are
 * generated from the input stream. This class is module-local on
 * purpose.
 *
 * @author Jens-S. Vöckler
 * @version $Revision: 50 $
 *
 */
class QueryScanner
{
  /**
   * stores the stream from which we are currently scanning.
   */
  private LineNumberReader m_in;

  /**
   * captures the look-ahead character;
   */
  private int m_lookAhead;

  /**
   * Starts to scan the given stream. 
   */
  public QueryScanner( java.io.Reader reader )
    throws IOException
  {
    this.m_in = new LineNumberReader(reader);
    this.m_lookAhead = m_in.read();
    // skipWhitespace();
  }

  /**
   * Obtains the current line number in the input stream from the outside.
   * @return the current line number.
   */
  public int getLineNumber()
  {
    return m_in.getLineNumber();
  }

  /**
   * Skips any white space and comments in the input. This method
   * stops either at the end of file, or at any non-whitespace 
   * input character.
   */
  private void skipWhitespace()
    throws IOException
  {
    // end of file?
    if ( m_lookAhead == -1 ) return;
    
    // skip over whitespace
    while ( m_lookAhead != -1 && Character.isWhitespace((char) m_lookAhead) )
      m_lookAhead = m_in.read();

    // skip over comments until eoln
    if ( m_lookAhead == '#' ) { 
      m_in.readLine();
      m_lookAhead = m_in.read();
      skipWhitespace(); // FIXME: reformulate end-recursion into loop
    }
  }

  /**
   * Checks for the availability of more input.
   * @return true, if there is more to read, false for EOF. 
   */
  public boolean hasMoreTokens()
    throws IOException
  {
    skipWhitespace();
    return ( this.m_lookAhead != -1 );
  }

  /**
   * Obtains the next token from the input stream.
   * @return an instance conforming to the token interface, or null for eof.
   * @throws IOException if something went wrong while reading 
   * @throws QueryScannerException if a lexical error was encountered.
   */
  public String nextToken()
    throws IOException, QueryScannerException
  {
    // sanity check
    skipWhitespace();
    if ( m_lookAhead == -1 ) return null;

    switch ( m_lookAhead ) {
    case '(':
      m_lookAhead = m_in.read();
      skipWhitespace();
      return "(";

    case ')':
      m_lookAhead = m_in.read();
      skipWhitespace();
      return ")";

    case '=':
      m_lookAhead = m_in.read();
      skipWhitespace();
      return "@EQ";

    case '>':
      m_lookAhead = m_in.read();
      if (m_lookAhead == '=') {
	  m_lookAhead = m_in.read();
	  skipWhitespace();
	  return "@GE";
      } else {
	  return "@GT";
      }
      	
    case '<':
      m_lookAhead = m_in.read();
      if (m_lookAhead == '=') {
	  m_lookAhead = m_in.read();
	  skipWhitespace();
	  return "@GE";
      } else if (m_lookAhead == '>') {
	  m_lookAhead = m_in.read();
	  skipWhitespace();
	  return "@NE";
      } else {
	  return "@LT";
      }

    case '!':
      m_lookAhead = m_in.read();
      if (m_lookAhead == '=') {
	  m_lookAhead = m_in.read();
	  skipWhitespace();
	  return "@NE";
      } else {
	  // '!' alone is not allowed
	  throw new QueryScannerException( m_in, "found character '!' without '='" );
      }
      	
    case '\"':
    case '\'':
      int ch = m_lookAhead; 
      // parse a quoted string
      StringBuffer result = new StringBuffer(16);

      do {
	m_lookAhead = m_in.read();
	if ( m_lookAhead == -1 || m_lookAhead == '\r' || m_lookAhead == '\n' ) {
	  // eof is an unterminated string
	  throw new QueryScannerException( m_in, "unterminated quoted string" );
	} else if ( m_lookAhead == '\\' ) {
	  m_lookAhead = m_in.read();
	  if ( m_lookAhead == -1 ) throw new QueryScannerException( m_in, "unterminated escape in quoted string" );
	  else result.append(m_lookAhead); // always add whatever is after the backslash
	} else if ( m_lookAhead != ch ) {
	  result.append((char) m_lookAhead);
	}
      } while ( m_lookAhead != ch );

      // skip over final quote
      m_lookAhead = m_in.read();
      skipWhitespace();
      return ( "$" + result.toString() );

    default:
      // are we parsing a reserved word or identifier
      if ( Character.isLetterOrDigit((char) m_lookAhead) || 
	   m_lookAhead=='_' || m_lookAhead=='-' || m_lookAhead == '.') {
        StringBuffer identifier = new StringBuffer(8);
        identifier.append( (char) m_lookAhead );
        m_lookAhead = m_in.read();
        while ( m_lookAhead != -1 &&
                ( Character.isLetterOrDigit((char) m_lookAhead) ||
                  m_lookAhead=='_' || m_lookAhead=='-' || m_lookAhead == '.' ||
		  m_lookAhead=='(' || m_lookAhead==')' ) )
	{
	    identifier.append( (char) m_lookAhead );
	    m_lookAhead = m_in.read();
	}
     
	// done parsing identifier or reserved word
	skipWhitespace();
	String s = identifier.toString();
	if ( s.compareToIgnoreCase("exists") == 0 )
	  return "@EX";
	else if ( s.compareToIgnoreCase("contains") == 0 ) 
	  // reserved word
	  return "@CT";
	else if ( s.compareToIgnoreCase("like") == 0 ) 
	  // reserved word
	  return "@LK";
	else if ( s.compareToIgnoreCase("between") == 0 ) 
	  // reserved word
	  return "@BT";
	else if ( s.compareToIgnoreCase("and") == 0 ) 
	  // reserved word
	  return "AND";
	else if ( s.compareToIgnoreCase("or") == 0 ) 
	  // reserved word
	  return "OR";
	else if ( s.compareToIgnoreCase("not") == 0 ) 
	  // reserved word
	  return "NOT";
	else
	  // is a non-reserved identifier
	  return ("#" + s);

      } else {
	// unknown material
	throw new QueryScannerException( m_in, "unknown character " + (char)m_lookAhead );
      }
    } // switch
  }
}
